const request = require('request-promise')
const config = require('config')
const nextcloud = config.get('nextcloud')
const cheerio = require('cheerio')
import {ncOptions,getBase64Auth} from '../common'
const capability_path = '/ocs/v2.php/cloud/capabilities'
const publicPath = config.get('nextcloud.share_path')
const path = require('path')

export const parseFilesFromMetaXml = (filesMeta,prefixPath,showHidden=false,groupShare=false,includes=[],excludes=[])=>{
    let $,href,file,filepath,match,basename,props,files=[],mount,owner
    $ = cheerio.load(filesMeta.data, {
        xmlMode: true
    })
    $("d\\:multistatus d\\:response").each((index,e)=>{
        href = $(e).find("d\\:href").text()
        file = {}
        file.type = 'file'
        if(href.substr(href.length-1)=='/'){
            file.type = "folder"
        }
        if(!href.startsWith(prefixPath)){
            return
        }
        filepath = href.replace(/\/$/, '').replace(prefixPath,'')||'/'
        file.path = decodeURIComponent(filepath)
        if(filepath=='/' || (filepath.match(/\/\./)&&!showHidden)){
            return
        }
        basename = path.basename(filepath)
        file.basename = decodeURIComponent(basename)
        if(includes&&includes.length){
            match = includes.some((pattern)=>{
                return checkPattern(basename,pattern)
            })
            if(file.type == "file"&&!match){
                return
            }
        }
        if(excludes&&excludes.length){
            match = excludes.every((pattern)=>{
                return !checkPattern(basename,pattern)
            })
            if(file.type == "file"&&!match){
                return
            }
        }
        props = $(e).find("d\\:propstat d\\:prop")
        file.fileid = $(props).find("oc\\:fileid").text()
        file.lastmod = $(props).find("d\\:getlastmodified").text()
        file.size = parseInt($(props).find("oc\\:size").text())
        file.contentType = $(props).find("d\\:getcontenttype").text()
        mount = $(props).find("nc\\:mount-type").text()
        owner = $(props).find("oc\\:owner-id").text()
        if(groupShare&&!mount){
            return
        }
        if(mount&&owner){
            file.owner = owner
        }
        files.push(file)
    })
    return files
}

const checkPattern = (filename,pattern)=>{
    let match = false;
    pattern = pattern.replace(/[-\/\\^$+?.()|[\]{}]/g, '\\$&');
    let stars = (pattern.match(/\*/g) || []).length;
    if(stars>1){
        throw new Error('only one star in pattern allowed')
    }
    if(pattern.startsWith('*')){
        pattern = pattern.substr(1) + '$'
    }else if(pattern.endsWith('*')){
        pattern = '^' + pattern.substr(0,pattern.length-1)
    }
    match = filename.match(new RegExp(pattern))
    return !!match
}

const buildOrder = (orderItem)=>{
    let {prop,ascending} = orderItem
    if(prop==='size'){
        prop = `<oc:size/>`
    }else if(prop==='lastmodified'){
        prop = `<d:getlastmodified/>`
    }else if(prop==='name'){
        prop = `<d:displayname/>`
    }else{
        throw new Error(`order by ${prop} not support`)
    }
    ascending = orderItem.ascending?`<d:ascending/>`:`<d:descending/>`
    return `<d:order>
                <d:prop>
                    ${prop}
                </d:prop>
                ${ascending}
            </d:order>`
}

const buildWhere = async (namePattern,owner)=>{
    let version = await getNextcloudVersion(),ownerPart='',patternPart=''
    if(version>='18'){
        ownerPart = `<d:eq>
                        <d:prop>
                            <oc:owner-id/>
                        </d:prop>
                        <d:literal>${owner}</d:literal>
                    </d:eq>`
    }
    patternPart = namePattern.map((pattern)=>{
        return `<d:like>
                    <d:prop>
                     <d:displayname/>
                     </d:prop>
                     <d:literal>${pattern.replace(/\*/g,'%')}</d:literal>
                </d:like>`
    }).join('')
    return `<d:where>
                 <d:and>
                    ${ownerPart}
                    <d:or>
                        ${patternPart}
                    </d:or>
                 </d:and>  
            </d:where>`
}

export const getNextcloudVersion = async()=>{
    let options = {...ncOptions}, result,version
    options.method = 'GET'
    options.headers.Authorization = getBase64Auth(nextcloud.user, nextcloud.password)
    options.uri = `${nextcloud.host}${capability_path}`
    result = await request(options)
    version = result&&result.ocs&&result.ocs.data&&result.ocs.data.version&&result.ocs.data.version.major
    return version
}

export const getFileContent = async(client,filePath,format='text') =>{
    let result = await client.getFileContents(filePath,{format})
    return result
}

export const getFileById = async (id)=>{
    let url = nextcloud.host + '/index.php/s/' + id + '/download',result
    result = await request(url)
    return result
}

export const getFileList = async (client, user,path,showHidden=false,groupShare=false,includes,excludes)=>{
    let filesMeta,files,prefixPath = groupShare?'':publicPath;
    filesMeta = await client.customRequest(path , {
        method: "PROPFIND",
        headers: {
            Accept: "text/xml",
            Depth: "infinity",
            "Content-Type": "application/xml; charset=utf-8"
        },
        data: `<?xml version="1.0" encoding="UTF-8"?>
                    <d:propfind  xmlns:d="DAV:" xmlns:oc="http://owncloud.org/ns" xmlns:nc="http://nextcloud.org/ns" xmlns:ocs="http://open-collaboration-services.org/ns">
                      <d:prop>
                        <oc:fileid />
                        <oc:tags />
                        <d:getlastmodified />
                        <oc:size />
                        <d:getcontenttype />
                        <d:displayname/>
                        <nc:mount-type />
                        <oc:owner-id />
                      </d:prop>
                    </d:propfind>`,
        responseType: "text"
    });
    files = parseFilesFromMetaXml(filesMeta,`/remote.php/dav/files/${user.id}${prefixPath}`,showHidden,groupShare,includes,excludes)
    return files
}

export const setFileName = async (client, url, name)=>{
    let result = await client.customRequest(url, {
        method: "PROPPATCH",
        headers: {
            Accept: "text/xml",
            "Content-Type": "application/xml; charset=utf-8"
        },
        data: `<?xml version="1.0" encoding="UTF-8"?>
                    <d:propertyupdate xmlns:d="DAV:" xmlns:oc="http://owncloud.org/ns">
                      <d:set>
                            <d:prop>
                               <d:displayname>${name}</d:displayname>
                            </d:prop>
                      </d:set>
                    </d:propertyupdate>`,
        responseType: "text"
    });
    return result
}

export const searchFile = async (client, user,body)=>{
    let filesMeta,files,wherePart,pattern=body&&body.pattern||['*'],
        orderBy = body.orderBy?body.orderBy:[{prop:'lastmodified',ascending:false}],
        showHidden = ((body.showHidden===true)?true:false),
        limit = body.limit||1000, offset = body.offset||0;
    orderBy = orderBy.map((orderItem)=>{
        return buildOrder(orderItem)
    }).join('')
    wherePart = await buildWhere(pattern,user.id)
    filesMeta = await client.customRequest('/' , {
        method: "SEARCH",
        headers: {
            Accept: "text/xml",
            Depth: "infinity",
            "Content-Type": "application/xml; charset=utf-8"
        },
        data: `<?xml version="1.0" encoding="UTF-8"?>
                    <d:searchrequest xmlns:d="DAV:" xmlns:oc="http://owncloud.org/ns" xmlns:sa="https://github.com/icewind1991/SearchDAV/ns">
                         <d:basicsearch>
                             <d:select>
                                 <d:prop>
                                    <oc:fileid />
                                    <d:getlastmodified />
                                    <d:resourcetype />
                                    <oc:size />
                                    <d:getcontenttype />
                                    <d:displayname/>
                                    <d:getcontentlength/>
                                 </d:prop>
                             </d:select>
                             <d:from>
                                 <d:scope>
                                     <d:href>/files/${user.id}</d:href>
                                     <d:depth>infinity</d:depth>
                                 </d:scope>
                             </d:from>
                             ${wherePart}
                             <d:orderby>
                                ${orderBy}
                            </d:orderby>
                            <d:limit>
                                <d:nresults>${limit}</d:nresults>
                                <sa:firstresult>${offset}</sa:firstresult>
                            </d:limit>
                        </d:basicsearch>
                    </d:searchrequest>`,
        responseType: "text"
    });
    files = parseFilesFromMetaXml(filesMeta,`/remote.php/dav/files/${user.id}/share`,showHidden)
    return files
}

export const checkExist = async (client,path)=>{
    let exist = await client.exists(path),error
    if(exist){
        error = new Error()
        error.status = 409
        error.message = 'file already exist'
        throw(error)
    }
}

export const getFilePreview = async(user,fileId,x=40,y=30)=>{
    let options = {headers:{}},result
    options.uri = nextcloud.host + "/index.php/core/preview"
    options.headers.Authorization = getBase64Auth(user.id, user.passwd)
    options.method = 'GET'
    options.qs = {
        fileId,x,y
    }
    options.encoding = null
    result = await request(options)
    return result
}

